Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix] exportMap: export map cache is tainted by unreliable parse results #3062

Merged
merged 1 commit into from
Sep 26, 2024

Conversation

michaelfaith
Copy link
Contributor

@michaelfaith michaelfaith commented Sep 15, 2024

This change addresses an issue observed with the v9 upgrade, where the ExportMap Cache is being tainted with a bad export map, if the parse doesn't yield a visitorKeys (which can happen if an incompatible parser is used (e.g. og babel eslint)). If one run of the no-cycle rule uses an incompatible parser and taints the cache and a subsequent test is run with a compatible parser, then the bad export map will be found in the cache and used instead of attempting to proceed with the parse.

I don't think this is likely to occur in the wild, but in the event that it does, this should help. One thing to note, though: if someone is using an incompatible parser, then this change will have a performance impact, since it will attempt to create an export map every time, until it yields one that has visitorKeys attached.

I also updated the getExports test to use a valid parserPath, rather than a mocked one, so the tests are acting on a valid parserPath. Otherwise the export map won't be cached following this change and the test for it caching would fail.

Additional context: #2996 (comment)

…sults

This change addresses and issue observed with the v9 upgrade, where the ExportMap Cache is being tainted with a bad export map, if the parse doesn't yield a `visitorKeys` (which can happen if an incompatible parser is used (e.g. og babel eslint)) for one run of the no-cycle rule.  If a subsequent test is run with a compatible parser, then the bad export map will be found in the cache and used instead of attempting to proceed with the parse.

I also updated the `getExports` test to use a valid parserPath, rather than a mocked one, so that the tests are acting on a valid parserPath.  Otherwise the export map won't be cached following this change.
@michaelfaith
Copy link
Contributor Author

michaelfaith commented Sep 15, 2024

The test failures are from eslint 2 and 3, which I'm guessing doesn't yield a visitorKeys from the parse, causing the cache to be bypassed (failing the test that the export map is cached).

Copy link

codecov bot commented Sep 15, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 90.72%. Comparing base (5c9757c) to head (8117601).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3062      +/-   ##
==========================================
- Coverage   92.21%   90.72%   -1.50%     
==========================================
  Files          82       82              
  Lines        3557     3557              
  Branches     1244     1244              
==========================================
- Hits         3280     3227      -53     
- Misses        277      330      +53     
Flag Coverage Δ
90.72% <100.00%> (-1.50%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@michaelfaith
Copy link
Contributor Author

michaelfaith commented Sep 15, 2024

Another idea for how to approach this, to limit the impact to just the rules that need it:
we could add an additional optional param to ExportMapBuilder.get that takes a skip cache function, which takes an export map and returns a boolean for whether that instance of the map should be cached. no-cycle could then pass in (exportMap) => !exportMap.visitorKeys, which has the same effect, but limits it to only that rule.

@G-Rath
Copy link
Contributor

G-Rath commented Sep 15, 2024

Confirming that this looks to address some of the test failures in #2996 - definitely the ones from no-cycle and probably others.

The test failures are from eslint 2 and 3

Could you just also check what version of ESLint is being used? as this seems to only take effect when using flat config and/or ESLint v9 which neither of those versions will support, and it feels like it could be more of a footgun to have rules control the cache

@michaelfaith
Copy link
Contributor Author

Could you just also check what version of ESLint is being used? as this seems to only take effect when using flat config and/or ESLint v9 which neither of those versions will support, and it feels like it could be more of a footgun to have rules control the cache

Yeah, I like that too.

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach seems fine to me. If a version check is the cleanest way to handle this, that's fine, but it'd be much nicer to feature-test instead.

@michaelfaith michaelfaith changed the title fix(utils): export map cache is tainted by unreliable parse results fix(core): export map cache is tainted by unreliable parse results Sep 16, 2024
@michaelfaith
Copy link
Contributor Author

michaelfaith commented Sep 16, 2024

This approach seems fine to me. If a version check is the cleanest way to handle this, that's fine, but it'd be much nicer to feature-test instead.

Updated the tests to only run the caching test if we determine babel-eslint has visitor-keys.

cc @G-Rath you may want to pull this into your PR, to resolve the v2/v3 test errors.

tests/src/core/getExports.js Outdated Show resolved Hide resolved
tests/src/core/getExports.js Outdated Show resolved Hide resolved
@michaelfaith michaelfaith force-pushed the fix/exportmap-cache branch 4 times, most recently from f6630cd to 89c3040 Compare September 25, 2024 22:22
@ljharb ljharb force-pushed the fix/exportmap-cache branch 2 times, most recently from ebd6131 to 61f02a2 Compare September 25, 2024 22:59
@ljharb ljharb changed the title fix(core): export map cache is tainted by unreliable parse results [Fix] exportMap: export map cache is tainted by unreliable parse results Sep 25, 2024
@ljharb ljharb merged commit 61f02a2 into import-js:main Sep 26, 2024
309 checks passed
@michaelfaith michaelfaith deleted the fix/exportmap-cache branch September 26, 2024 21:44
renovate bot added a commit to andrei-picus-tink/auto-renovate that referenced this pull request Oct 4, 2024
| datasource | package              | from   | to     |
| ---------- | -------------------- | ------ | ------ |
| npm        | eslint-plugin-import | 2.30.0 | 2.31.0 |


## [v2.31.0](https://github.com/import-js/eslint-plugin-import/blob/HEAD/CHANGELOG.md#2310---2024-10-03)

##### Added

-   support eslint v9 (\[[#2996](import-js/eslint-plugin-import#2996)], thanks \[[@G-Rath](https://github.com/G-Rath)] \[[@michaelfaith](https://github.com/michaelfaith)])
-   \[`order`]: allow validating named imports (\[[#3043](import-js/eslint-plugin-import#3043)], thanks \[[@manuth](https://github.com/manuth)])
-   \[`extensions`]: add the `checkTypeImports` option (\[[#2817](import-js/eslint-plugin-import#2817)], thanks \[[@phryneas](https://github.com/phryneas)])

##### Fixed

-   `ExportMap` / flat config: include `languageOptions` in context (\[[#3052](import-js/eslint-plugin-import#3052)], thanks \[[@michaelfaith](https://github.com/michaelfaith)])
-   \[`no-named-as-default`]: Allow using an identifier if the export is both a named and a default export (\[[#3032](import-js/eslint-plugin-import#3032)], thanks \[[@akwodkiewicz](https://github.com/akwodkiewicz)])
-   \[`export`]: False positive for exported overloaded functions in TS (\[[#3065](import-js/eslint-plugin-import#3065)], thanks \[[@liuxingbaoyu](https://github.com/liuxingbaoyu)])
-   `exportMap`: export map cache is tainted by unreliable parse results (\[[#3062](import-js/eslint-plugin-import#3062)], thanks \[[@michaelfaith](https://github.com/michaelfaith)])
-   `exportMap`: improve cacheKey when using flat config (\[[#3072](import-js/eslint-plugin-import#3072)], thanks \[[@michaelfaith](https://github.com/michaelfaith)])
-   adjust "is source type module" checks for flat config (\[[#2996](import-js/eslint-plugin-import#2996)], thanks \[[@G-Rath](https://github.com/G-Rath)])

##### Changed

-   \[Docs] \[`no-relative-packages`]: fix typo (\[[#3066](import-js/eslint-plugin-import#3066)], thanks \[[@joshuaobrien](https://github.com/joshuaobrien)])
-   \[Performance] \[`no-cycle`]: dont scc for each linted file (\[[#3068](import-js/eslint-plugin-import#3068)], thanks \[[@soryy708](https://github.com/soryy708)])
-   \[Docs] \[`no-cycle`]: add `disableScc` to docs (\[[#3070](import-js/eslint-plugin-import#3070)], thanks \[[@soryy708](https://github.com/soryy708)])
-   \[Tests] use re-exported `RuleTester` (\[[#3071](import-js/eslint-plugin-import#3071)], thanks \[[@G-Rath](https://github.com/G-Rath)])
-   \[Docs] \[`no-restricted-paths`]: fix grammar (\[[#3073](import-js/eslint-plugin-import#3073)], thanks \[[@unbeauvoyage](https://github.com/unbeauvoyage)])
-   \[Tests] \[`no-default-export`], \[`no-named-export`]:  add test case (thanks \[[@G-Rath](https://github.com/G-Rath)])
renovate bot added a commit to andrei-picus-tink/auto-renovate that referenced this pull request Oct 4, 2024
| datasource | package              | from   | to     |
| ---------- | -------------------- | ------ | ------ |
| npm        | eslint-plugin-import | 2.30.0 | 2.31.0 |


## [v2.31.0](https://github.com/import-js/eslint-plugin-import/blob/HEAD/CHANGELOG.md#2310---2024-10-03)

##### Added

-   support eslint v9 (\[[#2996](import-js/eslint-plugin-import#2996)], thanks \[[@G-Rath](https://github.com/G-Rath)] \[[@michaelfaith](https://github.com/michaelfaith)])
-   \[`order`]: allow validating named imports (\[[#3043](import-js/eslint-plugin-import#3043)], thanks \[[@manuth](https://github.com/manuth)])
-   \[`extensions`]: add the `checkTypeImports` option (\[[#2817](import-js/eslint-plugin-import#2817)], thanks \[[@phryneas](https://github.com/phryneas)])

##### Fixed

-   `ExportMap` / flat config: include `languageOptions` in context (\[[#3052](import-js/eslint-plugin-import#3052)], thanks \[[@michaelfaith](https://github.com/michaelfaith)])
-   \[`no-named-as-default`]: Allow using an identifier if the export is both a named and a default export (\[[#3032](import-js/eslint-plugin-import#3032)], thanks \[[@akwodkiewicz](https://github.com/akwodkiewicz)])
-   \[`export`]: False positive for exported overloaded functions in TS (\[[#3065](import-js/eslint-plugin-import#3065)], thanks \[[@liuxingbaoyu](https://github.com/liuxingbaoyu)])
-   `exportMap`: export map cache is tainted by unreliable parse results (\[[#3062](import-js/eslint-plugin-import#3062)], thanks \[[@michaelfaith](https://github.com/michaelfaith)])
-   `exportMap`: improve cacheKey when using flat config (\[[#3072](import-js/eslint-plugin-import#3072)], thanks \[[@michaelfaith](https://github.com/michaelfaith)])
-   adjust "is source type module" checks for flat config (\[[#2996](import-js/eslint-plugin-import#2996)], thanks \[[@G-Rath](https://github.com/G-Rath)])

##### Changed

-   \[Docs] \[`no-relative-packages`]: fix typo (\[[#3066](import-js/eslint-plugin-import#3066)], thanks \[[@joshuaobrien](https://github.com/joshuaobrien)])
-   \[Performance] \[`no-cycle`]: dont scc for each linted file (\[[#3068](import-js/eslint-plugin-import#3068)], thanks \[[@soryy708](https://github.com/soryy708)])
-   \[Docs] \[`no-cycle`]: add `disableScc` to docs (\[[#3070](import-js/eslint-plugin-import#3070)], thanks \[[@soryy708](https://github.com/soryy708)])
-   \[Tests] use re-exported `RuleTester` (\[[#3071](import-js/eslint-plugin-import#3071)], thanks \[[@G-Rath](https://github.com/G-Rath)])
-   \[Docs] \[`no-restricted-paths`]: fix grammar (\[[#3073](import-js/eslint-plugin-import#3073)], thanks \[[@unbeauvoyage](https://github.com/unbeauvoyage)])
-   \[Tests] \[`no-default-export`], \[`no-named-export`]:  add test case (thanks \[[@G-Rath](https://github.com/G-Rath)])
renovate bot added a commit to mmkal/eslint-plugin-mmkal that referenced this pull request Oct 30, 2024
##### [v4.4.0](https://github.com/un-ts/eslint-plugin-import-x/blob/HEAD/CHANGELOG.md#440)

##### Minor Changes

-   [#169](un-ts/eslint-plugin-import-x#169) [`9c58269`](un-ts/eslint-plugin-import-x@9c58269) Thanks [@teidesu](https://github.com/teidesu)! - Add new rule option `checkTypedImports` for `extensions`, backports import-js/eslint-plugin-import#2817

##### Patch Changes

-   [#171](un-ts/eslint-plugin-import-x#171) [`9715220`](un-ts/eslint-plugin-import-x@9715220) Thanks [@SukkaW](https://github.com/SukkaW)! - Perf: avoid regexp during parser choosing

-   [#171](un-ts/eslint-plugin-import-x#171) [`9715220`](un-ts/eslint-plugin-import-x@9715220) Thanks [@SukkaW](https://github.com/SukkaW)! - Add extra guard for rule `no-named-as-default`. A few guards are borrowed from import-js/eslint-plugin-import#3032, but we don't sync the rest of changes from upstream since we have already implemented a way more performant check.

-   [#171](un-ts/eslint-plugin-import-x#171) [`9715220`](un-ts/eslint-plugin-import-x@9715220) Thanks [@SukkaW](https://github.com/SukkaW)! - More test cases for `no-named-export` and `no-defualt-export` rule specifically with non-module `sourceType`

-   [#171](un-ts/eslint-plugin-import-x#171) [`9715220`](un-ts/eslint-plugin-import-x@9715220) Thanks [@SukkaW](https://github.com/SukkaW)! - Fix `export` when there is only one `TSDeclareFunction` (import-js/eslint-plugin-import#3065)

-   [#171](un-ts/eslint-plugin-import-x#171) [`9715220`](un-ts/eslint-plugin-import-x@9715220) Thanks [@SukkaW](https://github.com/SukkaW)! - Prevent `ExportMap`'s cache is being tainted by incompatible parser (e.g. old `babel-eslint`). The cache is now skipped w/ incompatible parsers, which might introduce performance impacts only for those who are using incompatible parsers. (import-js/eslint-plugin-import#3062)

-   [#171](un-ts/eslint-plugin-import-x#171) [`9715220`](un-ts/eslint-plugin-import-x@9715220) Thanks [@SukkaW](https://github.com/SukkaW)! - Docs: fix a few typos here and there

-   [#168](un-ts/eslint-plugin-import-x#168) [`5de039c`](un-ts/eslint-plugin-import-x@5de039c) Thanks [@hyoban](https://github.com/hyoban)! - Fixes un-ts/eslint-plugin-import-x#167, the `no-duplicates` rule now allows co-existing inline type imports and namespace imports.

-   [#171](un-ts/eslint-plugin-import-x#171) [`9715220`](un-ts/eslint-plugin-import-x@9715220) Thanks [@SukkaW](https://github.com/SukkaW)! - Properly fix espree parser w/ ESLint Flat Config
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants